#
#   jwt.py
#   journalismManger
#
#   Created by luoxh on 2019-07-22-10:03.
#   Copyright © 2019 luoxh. All rights reserved.
#

from collections import OrderedDict
from datetime import datetime

from flask import current_app, jsonify, request
from flask_jwt import _default_jwt_decode_handler, _default_jwt_encode_handler, _default_jwt_headers_handler, JWT as _JWT, JWTError
from mongoengine import DoesNotExist

from src.model.user import User
from src.viewmodel.user import UserVM
from .exceptions import CustomException
from .status import CustomStatus


class JWT(_JWT):

    def __init__(self, app=None):
        self.jwt_encode_callback = _default_jwt_encode_handler
        self.jwt_decode_callback = _default_jwt_decode_handler
        self.jwt_headers_callback = _default_jwt_headers_handler

        if app is not None: self.init_app(app)

    def auth_request_callback(self):
        try:
            username = request.data[current_app.config.get('JWT_AUTH_USERNAME_KEY', 'username')]
            password = request.data[current_app.config.get('JWT_AUTH_PASSWORD_KEY', 'password')]
        except KeyError: raise CustomException(CustomStatus.PARAMETER_ERROR)

        _identity = self.authentication_callback(username, password)
        if not _identity: raise CustomException(CustomStatus.JWT_ERROR)
        access_token = self.jwt_encode_callback(_identity)
        return self.auth_response_callback(access_token, _identity)

    def authentication_callback(self, username, password):
        try: user = User.objects.get(username=username)
        except DoesNotExist: raise CustomException(CustomStatus.USERNAME_DOES_NOT_EXIST)
        if user.check_password(password): return UserVM(user)

    def jwt_payload_callback(self, identity):
        iat = datetime.utcnow()
        exp = iat + current_app.config.get('JWT_EXPIRATION_DELTA')
        nbf = iat + current_app.config.get('JWT_NOT_BEFORE_DELTA')
        identity = identity['id']
        return {'exp': exp, 'iat': iat, 'nbf': nbf, 'identity': identity}

    def auth_response_callback(self, access_token, identity):
        print(identity)
        return jsonify(
            {'code': '200', 'data': {'token': access_token.decode('utf-8'), 'username': identity['username']}})

    def request_callback(self):
        print('request_callback')
        auth_header_value = request.headers.get('Authorization', None)
        if not auth_header_value: auth_header_value = request.data.get('token', None)
        if not auth_header_value: auth_header_value = request.args.get('token', None)
        auth_header_prefix = current_app.config['JWT_AUTH_HEADER_PREFIX']
        if not auth_header_value: return
        parts = auth_header_value.split()
        if parts[0].lower() != auth_header_prefix.lower(): raise JWTError('Invalid JWT header',
                                                                          'Unsupported authorization type')
        elif len(parts) == 1: raise JWTError('Invalid JWT header', 'Token missing')
        elif len(parts) > 2: raise JWTError('Invalid JWT header', 'Token contains spaces')
        return parts[1]

    def jwt_error_callback(self, error):
        return jsonify(OrderedDict([('code', error.status_code), ('msg', error.description), (
            'request', request.method + ' ' + current_app.get_url_no_param()), ])), error.status_code, error.headers

    def identity_callback(self, payload):
        print('identity_callback')
        user_id = payload['identity']
        return User.objects.get(pk=user_id)


jwt = JWT()
